﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Configuration;
using System.Data;
using System.ComponentModel;

namespace KCLIMTrading.Mgmt
{
	public class B2BManager
	{
		#region Fields and properties
		private WebHeaderCollection headers;
		private KeyValuePair<string,string> cookie;
		private bool isAuthenticated;
		/// <summary>
		/// Return true if login successfully othwerise false.
		/// </summary>
		public bool IsAuthenticated {get {return this.isAuthenticated;}}
		
		private static Database database;
		/// <summary>
		/// Gets or sets database object.
		/// </summary>
		public Database Database {get{return database;} set {database = value;}}
		
		/// <summary>
		/// Current PO send to print.
		/// </summary>
		private PurchaseOrder selectedPO;
		private System.Drawing.Printing.PrintDocument printDocument;
		/// <summary>
		/// Gets PrintDocument for print output.
		/// </summary>
		public System.Drawing.Printing.PrintDocument PrintDocument {get {return this.printDocument;} }
		#endregion
		
		public B2BManager()
		{
			//default declaration.
			this.headers = new WebHeaderCollection();
			this.cookie = new KeyValuePair<string, string>(string.Empty,string.Empty);
			
			this.printDocument = new System.Drawing.Printing.PrintDocument();
			this.printDocument.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(doc_PrintPage);
			
			if(database == null) database = new Database();

			Login();
		}
		
		#region Methods
		/// <summary>
		/// Login into website and get the session cookie.
		/// </summary>
		/// <returns></returns>
		private bool Login()
		{
			string html = string.Empty;
			string url = "http://giant.b2b.com.my/esupplier/pages/common/loginAction.do?txtLoginCompanyId={0}&txtLoginUserId={1}&txtLoginPassword={2}";
			string companyId = ConfigurationManager.AppSettings["CompanyID"].ToString();
			string userId = ConfigurationManager.AppSettings["UserID"].ToString();
			string password = ConfigurationManager.AppSettings["Password"].ToString();
			
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(url,companyId,userId,password));
			request.Method = "POST";//request.AllowAutoRedirect = true;
			using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
			{
				using(StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
					html = reader.ReadToEnd();
				for(int i=0;i<response.Headers.Count;i++)
				{
					if(response.Headers.AllKeys[i] == "Set-Cookie")
						this.cookie = new KeyValuePair<string, string>("Cookie",response.Headers.Get(i));
				}
				response.Close();
			}
			
			this.isAuthenticated = (html.Length > 0) ? true:false;
			System.Diagnostics.Debug.WriteLine("login: " + isAuthenticated);
			return this.isAuthenticated;
		}
		/// <summary>
		/// Get new pending PO.
		/// </summary>
		/// <remarks>
		/// All the extract processing are based on the html rendered output. Logic below will fail if owner change the output.
		/// </remarks>
		/// <returns>Result count.</returns>
		public int GetPendingPO()
		{
			int count = 0;

			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://giant.b2b.com.my/esupplier/pages/po/ListPOPage.do");
			request.Headers.Add(cookie.Key, cookie.Value);

			List<string> validateLines = new List<string>();
			using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
			{
				using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
				{
					//grab order require to validate only
					string line = string.Empty;
					while ((line = reader.ReadLine()) != null)
					{
						if (line.Contains("validate_order"))
							validateLines.Add(line);
					}
				}
			}

			foreach (string line in validateLines)
			{
				//match number for 7 digit long only
				Match match = Regex.Match(line, "[0-9]{7}");
				if (match.Success)
				{
					//insert at first index to sort date desc
					PurchaseOrder po = GetPO(Convert.ToInt32(match.Groups[0].Value));
					database.PurchaseOrders.Insert(0,po);//database.PurchaseOrders.Add(po);
					count++;
				}
			}
			
			//by default sort by DeliveryDate desc
			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PurchaseOrder));
			PropertyDescriptor property = properties.Find("DeliveryDate",false);
			database.PurchaseOrders.Sort(new PropertyComparer<PurchaseOrder>(property, ListSortDirection.Descending));

			return count;
		}
		/// <summary>
		/// Get the lastest number of PO in the list of page from web.
		/// </summary>
		/// <param name="last">Number of latest PO to get.</param>
		/// <returns></returns>
		public int GetLastPO(int last)
		{
			int count = 0;
			
			int page = 0;
			List<int> ids = new List<int>();
			while(last>page)
			{
				page += 50;
				string url = string.Format(
					"https://giant.b2b.com.my/esupplier/pages/po/ListPOPage.do?poId=&poNo=&buyerOrgId=&sortField=PO.orderDate&sortDirection=DESC&directPage=&sstart=&sstatus=%25&isExpired=-1&start={0}&start1=1",
					page);
				System.Diagnostics.Debug.WriteLine("accessing "+url);
				HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);//"https://giant.b2b.com.my/esupplier/pages/po/ListPOPage.do");
				request.Headers.Add(cookie.Key, cookie.Value);
				
				string html = string.Empty;
				List<string> validateLines = new List<string>();
				using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
				{
					using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
					{
						//grab order require to validate only
						string line = string.Empty;
						while ((line = reader.ReadLine()) != null)
							html += line;
					}
				}
				//System.Diagnostics.Debug.WriteLine(html);
				
				//get collection of last 50 PO id
				Regex regex = new Regex("javascript:viewOrder\\(\\'[0-9]{7}\',\'GIANT\'\\)");
				foreach(Match match in regex.Matches(html))
				{
					Match m = Regex.Match(match.Value,"[0-9]{7}");
					if(m.Success) ids.Add(Convert.ToInt32(m.Groups[0].Value));
				}
			}
			
			foreach(int id in ids)
			{
				count++;
				PurchaseOrder po = GetPO(id);
				database.PurchaseOrders.Add(po);
			}
			
			return count;
		}
		/// <summary>
		/// Get PO details from web.
		/// </summary>
		/// <param name="id">PO id.</param>
		/// <returns></returns>
		private PurchaseOrder GetPO(int id)
		{
			System.Diagnostics.Debug.WriteLine("retrieving po id: "+id+" ...");
			PurchaseOrder po = new PurchaseOrder();
			po.Id = id;
			//string url = "https://giant.b2b.com.my/esupplier/pages/po/ListPOPage.do?poId={0}&poNo=&buyerOrgId=GIANT&sortField=PO.orderDate&sortDirection=DESC&directPage=ListPOPage.do&sstart=0&sstatus=%25&isExpired=-1&start=0&start1=1";
			string url = "https://giant.b2b.com.my/esupplier/pages/po/POSharedPage.do?poId={0}&buyerOrgId=GIANT&actionPage=View";
			
			string html = string.Empty;
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(url,id));
			request.Headers.Add(cookie.Key,cookie.Value);
			using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
			{
				using(StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
				{
					string line = string.Empty;
					bool isRecordingPO = false;
					string poLines = string.Empty;
					//grab not HTML tag which is value in cell only
					Regex regex = new Regex("(?<=^|>)[^><]+?(?=<|$)");
					while( (line = reader.ReadLine()) != null)
					{
						//extract PO Number, Delivery, and Cancellation Date
						if(line.Contains("PURCHASE ORDER"))
						{
							List<string> cells = new List<string>();
							foreach(Match match in regex.Matches(line))
							{
								if(!match.Value.Contains("PURCHASE ORDER"))
									cells.Add(match.Value);
							}
							
							if(cells.Count>2) po.Number = cells[2];
							if(cells.Count>8) po.DeliveryDate = ConvertToDate(cells[8]);
							if(cells.Count>11) po.CancelationDate = ConvertToDate(cells[11]);
						}
						
						if(line.Contains("Store Code"))
						{
							List<string> cells = new List<string>();
							foreach(Match match in regex.Matches(line))
							{
								if(match.Value.Trim().Length>0)
									cells.Add(match.Value);
							}
							
							//get customer code
							for(int i=0;i<cells.Count;i++)
							{
								if(cells[i] == "Store Code")
								{
									Customer customer = new Customer();
									customer.Code = Convert.ToInt32(cells[i+2]);
									po.SoldTo = customer;
									break;
								}
							}
						}
						
						
						if(line.Contains("0001</div>"))
							isRecordingPO = true;
						if(isRecordingPO)
							poLines += line;
						//let's include the last line until contains "Total Net Value"
						if(line.Contains("Total Net Value"))
							isRecordingPO = false;
					}
					
					//processing line items
					//System.Diagnostics.Debug.WriteLine(poLines);
					List<string> poCells = new List<string>();
					foreach(Match match in regex.Matches(poLines))
					{
						if(match.Value.Trim().Length>0)
							poCells.Add(match.Value.Trim());
					}
					for(int i=0;i<poCells.Count;i++)
					{
						//System.Diagnostics.Debug.WriteLine(poCells[i]);
						Match m = Regex.Match(poCells[i], "[0-9]{4}");
						if(m.Success && poCells[i].Length == 4)
						{
							Product product = new Product();
							product.Name = RemoveWhiteSpace(poCells[i+1]);
							product.Code = RemoveWhiteSpace(poCells[i+2]);
							product.Price = Convert.ToDecimal(poCells[i+6]);
							
							Item item = new Item();
							item.Product = product;
							item.Quantity = (int)Convert.ToDecimal(poCells[i+3]);
							item.UnitOfMeasure = poCells[i+4];
							
							po.Items.Add(item);
							
							//add in Product list automatically if it is new found
							database.Add(product);
						}
						
						//html = reader.ReadToEnd();
					}//end loops
				}//end StreadeReader
			}//end request.GetResponse
			//System.Diagnostics.Debug.WriteLine(html);
			
			return po;
		}
		public void Print(PurchaseOrder po)
		{
			this.selectedPO = po;
			this.printDocument.Print();
		}
		#endregion
		
		#region Functions
		/// <summary>
		/// Convert Malaysia display date format to a valid date object.
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		private DateTime ConvertToDate(string date)
		{
			string[] pieces = date.Split(new char[]{'.','/','-'});
			if(pieces.Length>2)
				return new DateTime(Convert.ToInt16(pieces[2]),
				                    Convert.ToInt16(pieces[1]),
				                    Convert.ToInt16(pieces[0]));
			else
				return DateTime.MinValue;//if fail
		}
		private string RemoveWhiteSpace(string sender)
		{
			string output = string.Empty;
			string[] pieces = sender.Split(new char[]{' '});
			foreach(string piece in pieces)
			{
				if(piece.Trim().Length>0)
					output += piece.Trim() + " ";
			}
			
			return output.Trim();
		}
		#endregion
		
		#region Events
		/// <summary>
		/// TODO: continue print each item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		/// <seealso cref="http://msdn.microsoft.com/en-us/library/system.windows.forms.printdialog%28v=vs.80%29.aspx">PrintDialog Class</seealso>
		/// <seealso cref="http://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.print%28v=vs.80%29.aspx">PrintDocument.Print Method</seealso>
		private void doc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
		{
			float top = 10;
			System.Diagnostics.Debug.WriteLine("start to print...");
			string text = "Id: "+selectedPO.Id;
			System.Drawing.Font printFont = new System.Drawing.Font("Calibri",10, System.Drawing.FontStyle.Regular);
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
			
			top += 12;
			text = "Number: "+selectedPO.Number;
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
			
			top += 12;
			text = "Delivery: "+selectedPO.DeliveryDate;
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
			
			top += 12;
			text = "Cancel: "+selectedPO.CancelationDate;
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
			
			top += 12;
			text = "Sold To: "+selectedPO.SoldTo;
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
			
			top += 12;
			text = "Items: "+selectedPO.Items.Count;
			e.Graphics.DrawString(text,printFont,System.Drawing.Brushes.Black,10,top);
		}
		#endregion
	}
}